import 'package:equatable/equatable.dart';

class ApiResponse<T> extends Equatable {
  final int code;
  final String message;
  final T? data;
  final bool success;
  final int? timestamp;

  const ApiResponse({
    required this.code,
    required this.message,
    this.data,
    required this.success,
    this.timestamp,
  });

  factory ApiResponse.fromJson(
    Map<String, dynamic> json,
    T Function(Object? json) fromJsonT,
  ) {
    return ApiResponse<T>(
      code: json['code'] as int? ?? 200,
      message: json['message'] as String? ?? 'Success',
      data: json['data'] != null ? fromJsonT(json['data']) : null,
      success: json['success'] as bool? ?? true,
      timestamp: json['timestamp'] as int?,
    );
  }

  Map<String, dynamic> toJson(Object? Function(T value) toJsonT) {
    return {
      'code': code,
      'message': message,
      'data': data != null ? toJsonT(data as T) : null,
      'success': success,
      'timestamp': timestamp,
    };
  }

  factory ApiResponse.success({
    T? data,
    String message = 'Success',
    int code = 200,
  }) {
    return ApiResponse(
      code: code,
      message: message,
      data: data,
      success: true,
      timestamp: DateTime.now().millisecondsSinceEpoch,
    );
  }

  factory ApiResponse.error({
    required String message,
    int code = -1,
    T? data,
  }) {
    return ApiResponse(
      code: code,
      message: message,
      data: data,
      success: false,
      timestamp: DateTime.now().millisecondsSinceEpoch,
    );
  }

  bool get isSuccess => success && code == 200;
  bool get isError => !success || code != 200;

  @override
  List<Object?> get props => [code, message, data, success, timestamp];
}

class PaginationResponse<T> extends Equatable {
  final List<T> items;
  final int total;
  final int page;
  final int pageSize;
  final int totalPages;
  final bool hasNext;
  final bool hasPrevious;

  const PaginationResponse({
    required this.items,
    required this.total,
    required this.page,
    required this.pageSize,
    required this.totalPages,
    required this.hasNext,
    required this.hasPrevious,
  });

  factory PaginationResponse.fromJson(
    Map<String, dynamic> json,
    T Function(Object? json) fromJsonT,
  ) {
    final itemsList = json['items'] as List<dynamic>? ?? [];
    final items = itemsList.map((item) => fromJsonT(item)).toList();
    
    return PaginationResponse<T>(
      items: items,
      total: json['total'] as int? ?? 0,
      page: json['page'] as int? ?? 1,
      pageSize: json['pageSize'] as int? ?? 20,
      totalPages: json['totalPages'] as int? ?? 0,
      hasNext: json['hasNext'] as bool? ?? false,
      hasPrevious: json['hasPrevious'] as bool? ?? false,
    );
  }

  Map<String, dynamic> toJson(Object? Function(T value) toJsonT) {
    return {
      'items': items.map((item) => toJsonT(item)).toList(),
      'total': total,
      'page': page,
      'pageSize': pageSize,
      'totalPages': totalPages,
      'hasNext': hasNext,
      'hasPrevious': hasPrevious,
    };
  }

  factory PaginationResponse.empty() {
    return const PaginationResponse(
      items: [],
      total: 0,
      page: 1,
      pageSize: 0,
      totalPages: 0,
      hasNext: false,
      hasPrevious: false,
    );
  }

  @override
  List<Object?> get props => [
        items,
        total,
        page,
        pageSize,
        totalPages,
        hasNext,
        hasPrevious,
      ];
}

class ApiResult<T> {
  final T? data;
  final String? errorMessage;
  final int? errorCode;
  final bool isSuccess;

  const ApiResult.success(this.data)
      : errorMessage = null,
        errorCode = null,
        isSuccess = true;

  const ApiResult.error(this.errorMessage, [this.errorCode])
      : data = null,
        isSuccess = false;

  bool get isError => !isSuccess;

  R when<R>({
    required R Function(T data) success,
    required R Function(String message, int? code) error,
  }) {
    if (isSuccess) {
      return success(data as T);
    } else {
      return error(errorMessage!, errorCode);
    }
  }

  ApiResult<R> map<R>(R Function(T data) mapper) {
    if (isSuccess) {
      try {
        return ApiResult.success(mapper(data as T));
      } catch (e) {
        return ApiResult.error(e.toString());
      }
    } else {
      return ApiResult.error(errorMessage!, errorCode);
    }
  }

  Future<ApiResult<R>> asyncMap<R>(Future<R> Function(T data) mapper) async {
    if (isSuccess) {
      try {
        final result = await mapper(data as T);
        return ApiResult.success(result);
      } catch (e) {
        return ApiResult.error(e.toString());
      }
    } else {
      return ApiResult.error(errorMessage!, errorCode);
    }
  }
}